using System;
using TestCases.CompareAssert.Core.Builders;
using TestCases.CompareAssert.Core.Interfaces;
using TestCases.CompareAssert.Core.Selectors;
using TestCases.Utils;

namespace TestCases.CompareAssert.Core.Comparers.Generics
{
    public class ComparableComparer : GenericComparer
    {
        public const string IdentifiableName = "ComparableComparer";
        private static readonly ComparableComparer _instance = new ComparableComparer();

// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
// ReSharper disable EmptyConstructor
        static ComparableComparer()
// ReSharper restore EmptyConstructor
        {
        }

        public ComparableComparer(): base(new CovariantInterfacesSelector("IComparable`1"))
        {
        }

        public static ComparableComparer Instance
        {
            get { return _instance; }
        }        
        
        public override string Name
        {
            get { return IdentifiableName; }
        }

        public override IResultBuilder Compare(ICompareContext context)
        {
            var result = CompareViaInterfaces(context);
            if (result != null)
            {
                return result;
            }
            
            if (context.LValue is IComparable)
            {                
                
                try
                {
                    return this.IsBoolResult(((IComparable) context.LValue).CompareTo(context.RValue) == 0, context).
                        ForEquals("<{LPath}>.CompareTo(<{RPath}>) is zero [{LValue}.CompareTo({RValue}) == 0].").
                        ForNotEquals("<{LPath}>.CompareTo(<{RPath}>) is not zero [{LValue}.CompareTo({RValue}) != 0].");
                }
                catch (ArgumentException)
                {                    
                }
                catch (InvalidCastException)
                {                    
                }
                
            }
            return this.IsNotApplicable(context).Left.IsNot("IComparable<{RType}> or IComparable");
        }

        public override IResultBuilder CompareVia(Type interfaceType, ICompareContext context)
        {
            return this.IsBoolResult(context.LValue.InvokeMethod<int>(interfaceType, "CompareTo", context.RValue) == 0, context).
                ForEquals("<{LPath}>.CompareTo(<{RPath}>) is zero [{LValue}.CompareTo({RValue}) == 0].").
                ForNotEquals("<{LPath}>.CompareTo(<{RPath}>) is not zero [{LValue}.CompareTo({RValue}) != 0].");
        }
    }
}